#include "peonstates.h"
#include "algorithms/AStar.h"
#include <cassert>
#include "misc/crudetimer.h"
#include "messagedispatcher.h"
#include "telegramtypes.h"


// Methods for IdleState
////////////////////////
IdleState* IdleState::Instance()
{
	static IdleState instance;

	return &instance;
}

/*-------------------------------------------------------------------------------------------------------------*/
void IdleState::Enter(Peon* pPeon)
{
	
}

/*-------------------------------------------------------------------------------------------------------------*/
void IdleState::Execute(Peon* pPeon)
{  

}

/*-------------------------------------------------------------------------------------------------------------*/
void IdleState::Exit(Peon* pPeon)
{

}

/*-------------------------------------------------------------------------------------------------------------*/
bool IdleState::OnMessage(Peon* pPeon, const Telegram& message)
{
	// Send msg to global message handler
	return false;
}

// Methods for MineState
////////////////////////
MineState* MineState::Instance()
{
	static MineState instance;

	return &instance;
}

/*-------------------------------------------------------------------------------------------------------------*/
void MineState::Enter(Peon* pPeon)
{
	pPeon->SetMineStartTime(Clock->GetCurrentTime());
	pPeon->SetFrame(17);
}

/*-------------------------------------------------------------------------------------------------------------*/
void MineState::Execute(Peon* pPeon)
{
	// The miner digs for gold until his capacity is full 
	
	double dStartingTime = pPeon->MineStartTime();
	double dCurrentTime = Clock->GetCurrentTime();

	if(dCurrentTime - dStartingTime > pPeon->MiningSpeed())
	{	
		int iGold = 1;
		pPeon->AddGold(iGold);
		pPeon->SetMineStartTime(dCurrentTime);
		MessageDispatcher::Instance()->DispatchMessage(0, ent_Peon, ent_GoldMine, msg_GoldMined, &iGold);
	}
	
	// If enough gold mined, go and put it in town
	if (pPeon->IsBagFull())
	{
		pPeon->GetFSM()->ChangeState(WalkState::Instance());
	}
}

/*-------------------------------------------------------------------------------------------------------------*/
void MineState::Exit(Peon* pPeon)
{
	
}

/*-------------------------------------------------------------------------------------------------------------*/
bool MineState::OnMessage(Peon* pPeon, const Telegram& message)
{
	// Send msg to global message handler
	return false;
}


// Methods for DepositState
///////////////////////////
DepositState* DepositState::Instance()
{
	static DepositState instance;

	return &instance;
}

/*-------------------------------------------------------------------------------------------------------------*/
void DepositState::Enter(Peon* pPeon)
{
	pPeon->SetFrame(20);
}

/*-------------------------------------------------------------------------------------------------------------*/
void DepositState::Execute(Peon* pPeon)
{ 
	pPeon->SetBagEmpty();
	pPeon->GetFSM()->ChangeState(WalkState::Instance());
}

/*-------------------------------------------------------------------------------------------------------------*/
void DepositState::Exit(Peon* pPeon)
{ 
	
}

/*-------------------------------------------------------------------------------------------------------------*/
bool DepositState::OnMessage(Peon* pPeon, const Telegram& message)
{
	// Send msg to global message handler
	return false;
}

// Methods for WalkState
///////////////////////////
WalkState* WalkState::Instance()
{
	static WalkState instance;

	return &instance;
}

/*-------------------------------------------------------------------------------------------------------------*/
void WalkState::Enter(Peon* pPeon)
{
	pPeon->SetFrame(0);

	AStar* pAStar;

	std::vector<AStarResult*> result;

	// Decide Path to take
	BaseGameEntity* pGoldMine = EntityManager::Instance()->GetEntityByID(ent_GoldMine);

	BaseGameEntity* pTownHall = EntityManager::Instance()->GetEntityByID(ent_TownHall);

	if(!pPeon->IsBagFull())
	{
		pPeon->SetDestination(pGoldMine->GetPosX(), pGoldMine->GetPosY());
	}

	if(pPeon->IsBagFull())
	{
		pPeon->SetDestination(pTownHall->GetPosX(), pTownHall->GetPosY());
	}
	
	// Star Algorithm
	int iCurrentCol = 0;
	int iCurrentRow = 0;

	int iDestinationCol = pPeon->GetDestinationCol();
	int iDestinationRow = pPeon->GetDestinationRow();

	float fPosX = pPeon->GetPosX();
	float fPosY = pPeon->GetPosY();

	Level::m_map.GetColRowFromWorldCoords(fPosX, fPosY, iCurrentCol, iCurrentRow);

	int columnCount = Level::m_map.GetMapWidth();
	int rowCount = Level::m_map.GetMapHeight();

	pAStar= new AStar(Level::m_tileCostList,
					  Level::m_map.GetMapWidth(),
					  Level::m_map.GetMapHeight(),
					  iCurrentCol,
					  iCurrentRow,
					  iDestinationCol,
					  iDestinationRow);

	result = pAStar->execute();

	for(int i=0; i < static_cast<int>(result.size()); i++)
	{
		pPeon->SetAStar(result[i]);
	}

	delete pAStar;
}

/*-------------------------------------------------------------------------------------------------------------*/
void WalkState::Execute(Peon* pPeon)
{ 
	std::vector<AStarResult*> result = pPeon->GetAStar();

	if(result.size() != 0)
	{
		float fCurrentPosX = pPeon->GetPosX();
		float fCurrentPosY = pPeon->GetPosY();

		int iCurrentCol;
		int iCurrentRow;

		int iDestinationCol = result[result.size() - 1]->getColumn();
		int iDestinationRow = result[result.size() - 1]->getRow();

		Level::m_map.GetColRowFromWorldCoords(fCurrentPosX, fCurrentPosY, iCurrentCol, iCurrentRow);

		static int direction = 0;

		if(iCurrentCol == iDestinationCol && iCurrentRow == iDestinationRow)
		{
			pPeon->PopAStar();
		}

		if(iDestinationCol - iCurrentCol > 0)
		{
			float fPosX = pPeon->WalkingSpeed() * pPeon->m_fDt;
			pPeon->SetPos(fPosX + fCurrentPosX, pPeon->GetPosY());

			if(direction != pPeon->m_iRight)
			{
				pPeon->SetAnimation(NULL);
				direction = pPeon->m_iRight;	
			}

			if(!pPeon->IsAnimationRunning())
			{
				pPeon->SetAnimation(&pPeon->m_walkright);
				
			}
		}

		if(iDestinationRow - iCurrentRow > 0)
		{
			float fPosY = pPeon->WalkingSpeed() * pPeon->m_fDt;
			pPeon->SetPos(pPeon->GetPosX(), -fPosY + fCurrentPosY);
			
			if(direction != pPeon->m_iDown)
			{
				pPeon->SetAnimation(NULL);
				direction = pPeon->m_iDown;
			}

			if(!pPeon->IsAnimationRunning())
			{
				pPeon->SetAnimation(&pPeon->m_walkdown);
			}
		}

		if(iDestinationCol - iCurrentCol < 0)
		{
			float fPosX = pPeon->WalkingSpeed() * pPeon->m_fDt;
			pPeon->SetPos(fCurrentPosX - fPosX, pPeon->GetPosY());

			if(direction != pPeon->m_iLeft)
			{
				pPeon->SetAnimation(NULL);
				direction = pPeon->m_iLeft;	
			}

			if(!pPeon->IsAnimationRunning())
			{
				pPeon->SetAnimation(&pPeon->m_walkleft);
			}
		}

		if(iDestinationRow - iCurrentRow < 0)
		{
			float fPosY = pPeon->WalkingSpeed() * pPeon->m_fDt;
			pPeon->SetPos(pPeon->GetPosX(), fCurrentPosY + fPosY);

			if(direction != pPeon->m_iUp)
			{
				pPeon->SetAnimation(NULL);
				direction = pPeon->m_iUp;
			}

			if(!pPeon->IsAnimationRunning())
			{
				pPeon->SetAnimation(&pPeon->m_walkup);
			}
		}
	}
	else
	{
		if(!pPeon->IsBagFull())
		{
			pPeon->GetFSM()->ChangeState(MineState::Instance());
		}

		if(pPeon->IsBagFull())
		{
			pPeon->GetFSM()->ChangeState(DepositState::Instance());
		}
	}
}

/*-------------------------------------------------------------------------------------------------------------*/
void WalkState::Exit(Peon* pPeon)
{ 
	pPeon->SetAnimation(NULL);
}

/*-------------------------------------------------------------------------------------------------------------*/
bool WalkState::OnMessage(Peon* pPeon, const Telegram& message)
{
	// Send msg to global message handler
	return false;
}

// Methods for PeonGlobalState
///////////////////////////
PeonGlobalState* PeonGlobalState::Instance()
{
	static PeonGlobalState instance;

	return &instance;
}

/*-------------------------------------------------------------------------------------------------------------*/
void PeonGlobalState::Enter(Peon* pPeon)
{

}

/*-------------------------------------------------------------------------------------------------------------*/
void PeonGlobalState::Execute(Peon* pPeon)
{ 

}

/*-------------------------------------------------------------------------------------------------------------*/
void PeonGlobalState::Exit(Peon* pPeon)
{ 

}

/*-------------------------------------------------------------------------------------------------------------*/
bool PeonGlobalState::OnMessage(Peon* pPeon, const Telegram& message)
{
	switch(message.m_iMessage)
	{
	case msg_GoldMineEmpty:

		if(pPeon->GetFSM()->CurrentState() != DepositState::Instance() || pPeon->GetFSM()->CurrentState() != MineState::Instance())
		{
			if(!pPeon->IsBagFull())
			{
				pPeon->GetFSM()->ChangeState(IdleState::Instance());
			}	
		}
		break;
	}
	
	// Send msg to global message handler
	return true;
}